choose-project.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import { useParams } from 'common'
  2. import { keyBy } from 'lodash'
  3. import { useCallback, useMemo } from 'react'
  4. import { toast } from 'sonner'
  5. import { ENV_VAR_RAW_KEYS } from '@/components/interfaces/Integrations/Vercel/Integrations-Vercel.constants'
  6. import { isVercelUrl } from '@/components/interfaces/Integrations/Vercel/VercelIntegration.utils'
  7. import ProjectLinker, {
  8. ForeignProject,
  9. } from '@/components/interfaces/Integrations/VercelGithub/ProjectLinker'
  10. import { Markdown } from '@/components/interfaces/Markdown'
  11. import VercelIntegrationWindowLayout from '@/components/layouts/IntegrationsLayout/VercelIntegrationWindowLayout'
  12. import { ScaffoldColumn, ScaffoldContainer } from '@/components/layouts/Scaffold'
  13. import { vercelIcon } from '@/components/to-be-cleaned/ListIcons'
  14. import { useOrgIntegrationsQuery } from '@/data/integrations/integrations-query-org-only'
  15. import { useIntegrationVercelConnectionsCreateMutation } from '@/data/integrations/integrations-vercel-connections-create-mutation'
  16. import { useVercelProjectsQuery } from '@/data/integrations/integrations-vercel-projects-query'
  17. import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
  18. import { BASE_PATH } from '@/lib/constants'
  19. import { EMPTY_ARR } from '@/lib/void'
  20. import { useIntegrationInstallationSnapshot } from '@/state/integration-installation'
  21. import type { NextPageWithLayout, Organization } from '@/types'
  22. const VERCEL_ICON = (
  23. <img src={`${BASE_PATH}/img/icons/vercel-icon.svg`} alt="Vercel Icon" className="w-4" />
  24. )
  25. const VercelIntegration: NextPageWithLayout = () => {
  26. const { slug, configurationId, next } = useParams()
  27. /**
  28. * Fetch the list of organization based integration installations for Vercel.
  29. *
  30. * Array of integrations installed on all
  31. */
  32. const { data: integrationData } = useOrgIntegrationsQuery({ orgSlug: slug })
  33. const { data } = useOrganizationsQuery({ enabled: slug !== undefined })
  34. const organization = data?.find((organization: Organization) => organization.slug === slug)
  35. const integration = integrationData?.find(
  36. (x) =>
  37. x.metadata !== undefined &&
  38. 'configuration_id' in x.metadata &&
  39. x.metadata?.configuration_id === configurationId
  40. )
  41. const { data: vercelProjectsData, isPending: isLoadingVercelProjectsData } =
  42. useVercelProjectsQuery(
  43. {
  44. organization_integration_id: integration?.id,
  45. },
  46. { enabled: integration?.id !== undefined }
  47. )
  48. const vercelProjects = useMemo(() => vercelProjectsData ?? EMPTY_ARR, [vercelProjectsData])
  49. const vercelProjectsById = useMemo(() => keyBy(vercelProjects, 'id'), [vercelProjects])
  50. const getForeignProjectIcon = useCallback(
  51. (_project: ForeignProject) => {
  52. const project = vercelProjectsById[_project.id]
  53. return !project?.framework ? (
  54. vercelIcon
  55. ) : (
  56. <img
  57. src={`${BASE_PATH}/img/icons/frameworks/${project.framework}.svg`}
  58. width={21}
  59. height={21}
  60. alt={`icon`}
  61. />
  62. )
  63. },
  64. [vercelProjectsById]
  65. )
  66. const snapshot = useIntegrationInstallationSnapshot()
  67. const { mutate: createConnections, isPending: isCreatingConnection } =
  68. useIntegrationVercelConnectionsCreateMutation({
  69. onSuccess() {
  70. if (next && isVercelUrl(next)) {
  71. snapshot.setLoading(false)
  72. window.location.href = next
  73. }
  74. },
  75. onMutate() {
  76. snapshot.setLoading(true)
  77. },
  78. onError(error) {
  79. snapshot.setLoading(false)
  80. toast.error(`Creating connection failed: ${error.message}`)
  81. },
  82. })
  83. const onCreateConnections = useCallback(
  84. (vars: any) => {
  85. createConnections({
  86. ...vars,
  87. connection: {
  88. ...vars.connection,
  89. metadata: {
  90. ...vars.connection.metadata,
  91. brivenConfig: {
  92. projectEnvVars: {
  93. write: true,
  94. },
  95. },
  96. },
  97. },
  98. })
  99. },
  100. [createConnections]
  101. )
  102. return (
  103. <>
  104. <ScaffoldContainer className="flex flex-col gap-6 grow py-8">
  105. <ScaffoldColumn className="max-w-[900px]! mx-auto w-full">
  106. <header>
  107. <h2>Create your first Project Connection</h2>
  108. <Markdown
  109. className="text-foreground-lighter"
  110. content={`
  111. This Briven integration manages your environment variables automatically to provide the latest keys in the unlikely event that you will need to refresh your JWT token.
  112. `}
  113. />
  114. </header>
  115. <ProjectLinker
  116. slug={organization?.slug}
  117. organizationIntegrationId={integration?.id}
  118. foreignProjects={vercelProjects}
  119. onCreateConnections={onCreateConnections}
  120. installedConnections={integration?.connections}
  121. isLoading={isCreatingConnection}
  122. integrationIcon={VERCEL_ICON}
  123. getForeignProjectIcon={getForeignProjectIcon}
  124. choosePrompt="Choose Vercel Project"
  125. onSkip={() => {
  126. if (next && isVercelUrl(next)) {
  127. window.location.href = next
  128. }
  129. }}
  130. loadingForeignProjects={isLoadingVercelProjectsData}
  131. mode="Vercel"
  132. />
  133. <Markdown
  134. content={`
  135. The following environment variables will be added:
  136. ${ENV_VAR_RAW_KEYS.map((x) => {
  137. return `\n - \`${x}\``
  138. })}
  139. `}
  140. />
  141. </ScaffoldColumn>
  142. </ScaffoldContainer>
  143. </>
  144. )
  145. }
  146. VercelIntegration.getLayout = (page) => (
  147. <VercelIntegrationWindowLayout>{page}</VercelIntegrationWindowLayout>
  148. )
  149. export default VercelIntegration